/**
* @file sample_process.cpp
*
* Copyright (C) 2020. Huawei Technologies Co., Ltd. All rights reserved.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "sample_process.h"
#include <iostream>
#include <fstream> // 包含ifstream和istringstream
#include <sstream>
#include <vector>
#include <string>
#include <new> // 用于operator new[]和operator delete[]
#include <cstring> // 包含memcpy
#include "model_process.h"
#include "acl/acl.h"
#include "utils.h"
#include "simu/simu.h"
#include "tcp_server.h"

using namespace std;
extern bool g_isDevice;

SampleProcess::SampleProcess() :deviceId_(0), context_(nullptr), stream_(nullptr)
{
    modelProcess = new ModelProcess();
}

SampleProcess::~SampleProcess()
{
    delete modelProcess;
    DestroyResource();
}

Result SampleProcess::InitResource()
{
    // ACL init
    const char *aclConfigPath = "../model/acl.json";
    aclError ret = aclInit(aclConfigPath);
    if (ret != ACL_SUCCESS) {
        ERROR_LOG("acl init failed, errorCode = %d", static_cast<int32_t>(ret));
        return FAILED;
    }
    INFO_LOG("acl init success");

    // set device
    ret = aclrtSetDevice(deviceId_);
    if (ret != ACL_SUCCESS) {
        ERROR_LOG("acl set device %d failed, errorCode = %d", deviceId_, static_cast<int32_t>(ret));
        return FAILED;
    }
    INFO_LOG("set device %d success", deviceId_);

    // create context (set current)
    ret = aclrtCreateContext(&context_, deviceId_);
    if (ret != ACL_SUCCESS) {
        ERROR_LOG("acl create context failed, deviceId = %d, errorCode = %d",
            deviceId_, static_cast<int32_t>(ret));
        return FAILED;
    }
    INFO_LOG("create context success");

    // create stream
    ret = aclrtCreateStream(&stream_);
    if (ret != ACL_SUCCESS) {
        ERROR_LOG("acl create stream failed, deviceId = %d, errorCode = %d",
            deviceId_, static_cast<int32_t>(ret));
        return FAILED;
    }
    INFO_LOG("create stream success");

    // get run mode
    // runMode is ACL_HOST which represents app is running in host
    // runMode is ACL_DEVICE which represents app is running in device
    aclrtRunMode runMode;
    ret = aclrtGetRunMode(&runMode);
    if (ret != ACL_SUCCESS) {
        ERROR_LOG("acl get run mode failed, errorCode = %d", static_cast<int32_t>(ret));
        return FAILED;
    }
    g_isDevice = (runMode == ACL_DEVICE);
    INFO_LOG("get run mode success");
    return SUCCESS;
}

int loadFrameFromFile(const std::string dsFn, void* buffer);

Result SampleProcess::Process()
{
    // model init
    // ModelProcess modelProcess;
    const char* omModelPath = "./model/mlp_lfm_001_sim_hw.om";
    Result ret = modelProcess->LoadModel(omModelPath);
    if (ret != SUCCESS) {
        ERROR_LOG("execute LoadModel failed");
        return FAILED;
    }
    ret = modelProcess->CreateModelDesc();
    if (ret != SUCCESS) {
        ERROR_LOG("execute CreateModelDesc failed");
        return FAILED;
    }
    // 生成输入数据缓冲区
    // size_t devBufferSize;
    // void *inputDevBuffer = nullptr;
    ret = modelProcess->GetInputSizeByIndex(0, devBufferSize);
    if (ret != SUCCESS) {
        ERROR_LOG("execute GetInputSizeByIndex failed");
        return FAILED;
    }
    aclError aclRet = aclrtMalloc(&inputDevBuffer, devBufferSize, ACL_MEM_MALLOC_HUGE_FIRST);
    if (aclRet != ACL_SUCCESS) {
        ERROR_LOG("malloc device buffer failed. size is %zu, errorCode is %d",
            devBufferSize, static_cast<int32_t>(aclRet));
        return FAILED;
    }
    ret = modelProcess->CreateInput(inputDevBuffer, devBufferSize);
    if (ret != SUCCESS) {
        ERROR_LOG("execute CreateInput failed");
        aclrtFree(inputDevBuffer);
        return FAILED;
    }
    // 生成输出缓冲区
    ret = modelProcess->CreateOutput();
    if (ret != SUCCESS) {
        aclrtFree(inputDevBuffer);
        ERROR_LOG("execute CreateOutput failed");
        return FAILED;
    }
    // 生成CPU端输入数据缓冲区
    void* inputBuffer = nullptr;
    aclError retMalloc = aclrtMallocHost(&inputBuffer, devBufferSize);
    if (inputBuffer == nullptr) {
        ERROR_LOG("malloc binFileBufferData failed, binFileBufferLen is %zu, errorCode is %d",
            devBufferSize, static_cast<int32_t>(ret));
        return FAILED;
    }



    startTcpServer(*this, inputBuffer);






    modelProcess->DestroyOutput();
    aclrtFree(inputDevBuffer);
    return SUCCESS;
}

Result SampleProcess::Infer(void* inputBuffer) {
    float *inputPtr = reinterpret_cast<float*>(inputBuffer);

    std::string dsFn = "work/datasets/irhw_dss/d00/d00/f14.txt";
    // loadFrameFromFile(dsFn, inputBuffer);
    std::cout << "??????????????????????????????????????????????????" << std::endl;
    aclError aclRet = aclrtMemcpy(inputDevBuffer, devBufferSize, inputBuffer, devBufferSize, ACL_MEMCPY_HOST_TO_DEVICE);
    if (aclRet != ACL_SUCCESS) {
        ERROR_LOG("memcpy failed. buffer size is %zu, errorCode is %d", devBufferSize, static_cast<int32_t>(aclRet));
        (void)aclrtFreeHost(inputBuffer);
        return FAILED;
    }
    // // 验证读入内容
    // float *ptr = reinterpret_cast<float*>(inputBuffer);
    // for (int i=0; i<max_floats; i++) {
    //     std::cout << "?????? " << i << ": " << *ptr << ";" << std::endl;
    //     ptr++;
    // }
    Result ret = modelProcess->Execute();
    if (ret != SUCCESS) {
        ERROR_LOG("execute inference failed!");
        aclrtFree(inputDevBuffer);
        return FAILED;
    }
    modelProcess->OutputModelResult();
    return SUCCESS;
}



void SampleProcess::DestroyResource()
{
    aclError ret;
    if (stream_ != nullptr) {
        ret = aclrtDestroyStream(stream_);
        if (ret != ACL_SUCCESS) {
            ERROR_LOG("destroy stream failed, errorCode = %d", static_cast<int32_t>(ret));
        }
        stream_ = nullptr;
    }
    INFO_LOG("end to destroy stream");

    if (context_ != nullptr) {
        ret = aclrtDestroyContext(context_);
        if (ret != ACL_SUCCESS) {
            ERROR_LOG("destroy context failed, errorCode = %d", static_cast<int32_t>(ret));
        }
        context_ = nullptr;
    }
    INFO_LOG("end to destroy context");

    ret = aclrtResetDevice(deviceId_);
    if (ret != ACL_SUCCESS) {
        ERROR_LOG("reset device %d failed, errorCode = %d", deviceId_, static_cast<int32_t>(ret));
    }
    INFO_LOG("end to reset device %d", deviceId_);

    ret = aclFinalize();
    if (ret != ACL_SUCCESS) {
        ERROR_LOG("finalize acl failed, errorCode = %d", static_cast<int32_t>(ret));
    }
    INFO_LOG("end to finalize acl");
}


int loadFrameFromFile(const std::string dsFn, void* buffer) {
    std::ifstream dsFo(dsFn);
    if (!dsFo.is_open()) {
        std::cerr << "Failed to open file: " << dsFn << std::endl;
        return 1;
    }
    float *ptr = static_cast<float*>(buffer);
    std::string line;
    while (std::getline(dsFo, line)) {
        std::istringstream iss(line);
        std::string value;
        while (std::getline(iss, value, ',')) {
            try {
                *ptr = std::stof(value);
                ptr++;
            } catch (const std::invalid_argument& ia) {
                std::cerr << "Invalid argument: " << value << " cannot be converted to float." << std::endl;
                return 1;
            } catch (const std::out_of_range& oor) {
                std::cerr << "Out of range: " << value << " is out of the range of float." << std::endl;
                return 1;
            }
        }
    }
    dsFo.close();
    return 0;
}
